﻿using System;
using System.Collections.Generic;
using System.Text;
using System.Threading;
using System.Threading.Tasks;

namespace Multithreading多线程
{
    /// <summary>
    /// 多线程事例
    /// </summary>
    public class MyExampleDoem
    {
        /// <summary>
        /// 多线程捕获异常 
        /// 多线程会将我们的异常吞了，因为我们的线程执行会直接执行完代码，不会去等待你捕获到我的异常。
        /// 我们的线程中最好是不要出现异常，自己处理好。
        /// </summary>
        public static void Show()
        {
            //创建一个多线程工厂
            TaskFactory taskFactory = new TaskFactory();
            //创建一个多线程容器
            List<Task> tasks = new List<Task>();
            //创建委托
            Action action = () =>
            {
                try
                {
                    string str = "sad";
                    int num = int.Parse(str);
                }
                catch (AggregateException ax)
                {
                    Console.WriteLine("我是AggregateException 我抓到了异常啦 ax:" + ax);
                }
                catch (Exception)
                {
                    Console.WriteLine("我是线程我已经报错了");
                }
            };
            //这个是我们经常需要做的捕获异常
            try
            {
                //创建10个多线程
                for (int i = 0; i < 10; i++)
                {
                    tasks.Add(taskFactory.StartNew(action));
                }
                Task.WaitAll(tasks.ToArray());
            }
            catch (Exception ex)
            {
                Console.WriteLine("异常啦");
            }
            Console.WriteLine("我已经执行完了");
        }

        /// <summary>
        /// 多线程取消机制 我们的Task在外部无法进行暂停 Thread().Abort() 无法很好控制，我们的线程。
        /// 如果我们使用一个全局的变量控制，就需要不断的监控我们的变量取消线程。
        /// 我们可以创建一个取消标记源，我们在创建线程的时候传入我们取消标记源Token
        /// Cancel() 取消线程，IsCancellationRequested 返回一个bool值，判断是不是取消了线程了
        /// </summary>
        public static void Show1()
        {
            //创建一个取消标记源
            CancellationTokenSource cancellationTokenSource = new CancellationTokenSource();
            //创建一个多线程工厂
            TaskFactory taskFactory = new TaskFactory();
            //创建一个多线程容器
            List<Task> tasks = new List<Task>();
            //创建委托
            Action<object> action = x =>
            {
                try
                {
                    //每个线程我等待2秒钟，不然
                    Thread.Sleep(2000);
                    //判断是不是取消线程了
                    if (cancellationTokenSource.IsCancellationRequested)
                    {
                        Console.WriteLine("放弃执行后面线程");
                        return;
                    }
                    if (Convert.ToUInt32(x) == 20)
                    {
                        throw new Exception(string.Format("{0} 执行失败", x));
                    }
                    Console.WriteLine("我是正常的我在执行");
                }
                catch (AggregateException ax)
                {
                    Console.WriteLine("我是AggregateException 我抓到了异常啦 ax:" + ax);
                }
                catch (Exception ex)
                {
                    //异常出现取消后面执行的所有线程
                    cancellationTokenSource.Cancel();
                    Console.WriteLine("我是线程我已经报错了");
                }
            };
            //这个是我们经常需要做的捕获异常
            try
            {
                //创建10个多线程
                for (int i = 0; i < 50; i++)
                {
                    int k = i;
                    tasks.Add(taskFactory.StartNew(action, k, cancellationTokenSource.Token));
                }
                Task.WaitAll(tasks.ToArray());
            }
            catch (Exception ex)
            {
                Console.WriteLine("异常啦");
            }
            Console.WriteLine("我已经执行完了");
        }

        /// <summary>
        /// 线程临时变量
        /// </summary>
        public static void Show2()
        {
            //创建一个线程工厂
            TaskFactory taskFactory = new TaskFactory();
            CancellationTokenSource cancellationTokenSource = new CancellationTokenSource();
            //创建一个委托
            Action<object> action = x =>
            {
                Console.WriteLine("传入参数 x:" + x);
            };
            for (int i = 0; i < 20; i++)
            {
                //这最主要的就是会创建20个k的临时变量
                int k = i;
                taskFactory.StartNew(action, k);
            }
            Console.ReadLine();
        }

        /// <summary>
        /// 创建一个静态对象，主要是用于锁代码块，如果是静态的就会全局锁，如果要锁实例类，就不使用静态就好了
        /// </summary>
        private readonly static object obj = new object();
        public static List<int> list = new List<int>();
        public static int count = 0;
        /// <summary>
        /// lock 多线程锁
        /// 当我们的线程访问同一个全局变量、同时访问同一个局部变量、同一个文件夹，就会出现线程不安全
        /// 我们的使用lock锁代码块的时候，尽量减少锁入代码块范围，因为我们锁代码之后会导致只有一个线程可以
        /// 访问到我们代码块了
        /// </summary>
        public static void Show3()
        {
            //创建线程容器
            List<Task> tasks = new List<Task>();
            //锁代码
            for (int i = 0; i < 10000; i++)
            {
                //添加线程
                tasks.Add(Task.Run(() =>
                {
                    //锁代码
                    //
                    lock (obj)
                    {
                        //这个里面就只会出现一个线程访问，资源。
                        list.Add(i);
                        count++;
                    }

                    //lock 是一个语法糖，就是下面的代码
                    Monitor.Enter(obj);
                    Monitor.Exit(obj);
                }));
            }
            Task.WaitAll(tasks.ToArray());
            Console.WriteLine("list 行数:" + list.Count + " count 总数:" + count);
            Console.ReadLine();
        }
    }
}
